package cn.ljy.authority.service.impl;

import cn.ljy.authority.config.security.TokenConfig;
import cn.ljy.authority.dao.mapper.SysConfigMapper;
import cn.ljy.authority.dao.mapper.SysUserMapper;
import cn.ljy.authority.model.SysConfig;
import cn.ljy.authority.model.dto.SysUserAuthority;
import cn.ljy.authority.service.SysUserTokenService;
import cn.ljy.common.exception.model.MyTokenException;
import cn.ljy.common.utils.jwt.JwtUtils;
import cn.ljy.common.utils.redis.StringRedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 设计用于管理账户的token验证
 * 设计在SysUserService层之上，即可以调用SysUserService，但是不能给其他业务层调用。
 */
@Service
@Slf4j
public class ISysUserTokenService extends IBaseAuthService implements SysUserTokenService {

    @Autowired
    private TokenConfig tokenConfig;
    @Autowired
    private SysConfigMapper sysConfigMapper;
    @Autowired
    private StringRedisUtils stringRedisUtils;
    @Autowired
    private SysUserMapper sysUserMapper;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;


    @Override
    public String saveUserToken(SysUserAuthority userAuthority) {
        String token=null;
        //生成token
        token=super.createUserToken(userAuthority);
        //生成要存入redis的key值
        String key=super.createUserRedisKeyFromToken(token);
        SysConfig config=sysConfigMapper.getByEnable();
        if(config.getAllowLoginInTheSameTime()){//如果允许一个账户同时登录,此处预留后续配置
            //将本次登陆账户的信息存储到redis中
            saveTokenInRedis(key,userAuthority);
        }else{//如果不允许一个账户同时登录
            //获取redis中所有前台登录用户的信息
            Set<String> keys = stringRedisTemplate.keys(tokenConfig.getAccessTokenKeyHeader().concat("*"));//获取redis中指定前缀开头的key
            //筛选出使用本账户名登录过并存储在redis中的所有key值
            keys=keys.stream().filter(keyOfEach->this.getUsername(super.getTokenFromRedisKey(keyOfEach)).equals(userAuthority.getUsername())).collect(Collectors.toSet());
            //先遍历该用户在reids中的登录信息，移除该用户之前登陆的信息。
            for(String keyOfEach:keys){
                //移除之前的key。
                stringRedisUtils.remove(keyOfEach);
                //获取该用户原来登录的客户端
//                String clientOrigin=stringRedisUtils.get(keyOfEach);
            }
            //将本次登陆账户的信息存储到redis中
            saveTokenInRedis(key,userAuthority);
        }
        return token;
    }

    @Override
    public void verifyUserToken(String token) {
        //验证token是否合法
        JwtUtils.verifyToken(token,tokenConfig.getJwtSecret());
        //从传递过来的token中检查redis中是否存在
        String redisKey=super.createUserRedisKeyFromToken(token);
        if(stringRedisUtils.exists(redisKey)) {//如果key还存在，即没有过期
            //判断管理账户的密码是否被修改，如果已经被修改，则强制下线（主要是其他登录的用户下线）
            SysUserAuthority userAuthorityOfOld=super.getSysUserAuthorityFromRedis(redisKey);//获取redis中保存的账户信息。注意保存的密码是加密的
            int number=sysUserMapper.countByUsernameAndPassword(userAuthorityOfOld.getUsername(), userAuthorityOfOld.getPassword());
            if (number==0) {//如果密码修改
                this.removeUserToken(token);//移除该用户存储在redis中的信息
                throw new MyTokenException("您已经被系统强制下线，原因是：您的密码已经在其他客户端被修改。请重新登录！");
            }
        }else{
            throw new MyTokenException("登录状态已失效，请重新登录");
        }
    }

    @Override
    public void verifyAndRefreshUserToken(String token) {
        //验证token是否合法
        verifyUserToken(token);
        //从传递过来的token中检查redis中是否存在
        String redisKey=super.createUserRedisKeyFromToken(token);
        //刷新key的时间为最长续航
        stringRedisUtils.expire(redisKey,tokenConfig.getExpireSeconds(),TimeUnit.SECONDS);
    }

    @Override
    public void removeUserToken(SysUserAuthority userAuthority) {
        //获取redis中所有前台登录用户的信息
        Set<String> keys = stringRedisTemplate.keys(tokenConfig.getAccessTokenKeyHeader().concat("*"));//获取redis中指定前缀开头的key
        //筛选出使用本账户名登录过并存储在redis中的所有key值
        keys=keys.stream().filter(keyOfEach->this.getUsername(super.getTokenFromRedisKey(keyOfEach)).equals(userAuthority.getUsername())).collect(Collectors.toSet());
        //先遍历该用户在reids中的登录信息，移除该用户之前登陆的信息。
        for(String keyOfEach:keys){
            //移除之前的key。
            stringRedisUtils.remove(keyOfEach);
            //获取该用户原来登录的客户端
//                String clientOrigin=stringRedisUtils.get(keyOfEach);
        }
    }

    @Override
    public void removeUserToken(String token) {
        if(StringUtils.hasLength(token)){//如果token不为空
            //验证token
            JwtUtils.verifyToken(token,tokenConfig.getJwtSecret());
            //从传递过来的token中检查redis中是否存在
            String key=super.createUserRedisKeyFromToken(token);
            stringRedisUtils.remove(key);
        }
    }

    @Override
    public String getUsername(String token) {
        //从token中读取账户信息
        return JwtUtils.getClaimByName(token,"username").asString();
    }
}
